package org.grow.commodity.util;


import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Scope;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;


/**
 * @Author: xwg
 * @CreateDate: 21-8-4
 */
@Slf4j
@Service
@Scope("prototype")
// 多线程安全的流水号生成器
public class CodeGenerator  {


    //    CodeGenerator::setApplicationContext
//    Spring容器 解决nextCode方法里依赖注入的核心容器

    //解决线程安全问题的核心容器
    //生成多例bean的方式，Spring容器负责生成bean，
//  本生成方法交给ThreadLocal,也意味着生成后的bean的管理工作
//  将交由ThreadLocal管理,而Spring容器将不再管理生成的多例bean.
//  这里一般是一个new函数,此处为了nextCode方法内能够享受Spring容器中的工具类.改用Spring生成
//  终上所述,此方法配合codeGeneratorContextHolder的初始化方法展示了A容器生成Bean交给B容器管理的思路
//    private static ThreadLocal<CodeGenerator> codeGeneratorContextHolder
//            = ThreadLocal.withInitial(()->StaticApplicationCtx.getBean(CodeGenerator.class));

//    核心对外使用方法 静态调用生成 不会有线程安全问题
//    public static String geneCode(String business){
//        return codeGeneratorContextHolder.get().nextCode(business);
//    }

//    何时运行这段代码,spring容器初始化的时候会对多例bean生成一个,不管之后有没有人额外调用.因此
//    此钩子函数将会被至少运行一次进行赋值,但是之后每生成一个多例bean就会重复调用一次并且赋值
//    但是这些次赋值从意义上来讲是没有意义的.




    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    public String nextCode(String business){

        if (business.length()!=2) throw new RuntimeException("业务号要为两位");

        Long l = System.currentTimeMillis();
        String s = l.toString();
        String substring = s.substring(4, s.length() - 1);
        Long v = stringRedisTemplate.opsForValue().increment("seed:"+business);
        String t = String.format("%s%s%06d", business.toUpperCase(),substring,v);
        return t;
    }




}
